// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Package intern通过将
// 一个较大的可比值（如16字节的字符串头）向下打包成一个全局唯一的8字节指针，可以生成较小的可比值。
// 
// 全局唯一指针使用弱
// 引用和终结符进行垃圾收集。这个包裹隐藏了那个。
package intern

import (
	"internal/godebug"
	"runtime"
	"sync"
	"unsafe"
)

// 值指针是指向基础可比较值的句柄。
// 有关如何使用值指针，请参见func Get。
type Value struct {
	_      [0]func() // 防止人们意外使用与之类似的值类型
	cmpVal any
	// 复活由mu保护（适用于所有值实例）。
	// 无论何时从uintptr合成v，它都被设置为真。
	resurrected bool
}

// Get返回传递给Get函数的可比值
// 该函数返回v。
func (v *Value) Get() any { return v.cmpVal }

// key是全局值映射中的一个key。
// 在将常用类型转换为空接口时，它包含类型专用字段，以避免分配
// 。
type key struct {
	s      string
	cmpVal any
	// isString报告键是否包含字符串。
	// 没有它，key的零值是不明确的。
	isString bool
}

// keyFor返回一个与cmpVal一起使用的密钥。
func keyFor(cmpVal any) key {
	if s, ok := cmpVal.(string); ok {
		return key{s: s, isString: true}
	}
	return key{cmpVal: cmpVal}
}

// Value返回一个由k生成的*值。
func (k key) Value() *Value {
	if k.isString {
		return &Value{cmpVal: k.s}
	}
	return &Value{cmpVal: k.cmpVal}
}

var (
	// mu guards valMap，一个由基础值构成的*值的weakref映射。
	// 它还保护所有*值的复活字段。
	mu      sync.Mutex
	valMap  = map[key]uintptr{} // 到uintptr（*值）
	valSafe = safeMap()         // 在安全+泄漏模式下非零
)

// 如果我们在安全但泄漏模式下，safeMap返回一个非零映射，
// 由GODEBUG=intern=leaky 
func safeMap() map[key]*Value {
	if godebug.Get("intern") == "leaky" {
		return map[key]*Value{}
	}
	return nil
}

// Get返回一个代表可比值cmpVal的指针。
// 
// 返回的指针对于Get（v）和Get（v2）将是相同的
// 当且仅当v==v2，并且可以用作映射键。
func Get(cmpVal any) *Value {
	return get(keyFor(cmpVal))
}

// GetByString与Get相同，只是它专门用于字符串。
// 这避免了将字符串放入接口{}
// 作为参数传递以获取的分配。
func GetByString(s string) *Value {
	return get(key{s: s, isString: true})
}

// 我们玩违反围棋规则的不安全游戏（并假设一个不移动的
// 收集器）。所以我们安静地走到这里。
// 有关更多实施细节，请参见下面的评论。
// go:nocheckptr 
func get(k key) *Value {
	mu.Lock()
	defer mu.Unlock()

	var v *Value
	if valSafe != nil {
		v = valSafe[k]
	} else if addr, ok := valMap[k]; ok {
		v = (*Value)(unsafe.Pointer(addr))
		v.resurrected = true
	}
	if v != nil {
		return v
	}
	v = k.Value()
	if valSafe != nil {
		valSafe[k] = v
	} else {
		// uintptr转换前的设置终结器（理论上的担忧；
		// 请参见https:
		runtime.SetFinalizer(v, finalize)
		valMap[k] = uintptr(unsafe.Pointer(v))
	}
	return v
}

func finalize(v *Value) {
	mu.Lock()
	defer mu.Unlock()
	if v.resurrected {
		// 我们输掉了比赛。当我们
		// 即将完成比赛时，有人重新启动了比赛。下一轮再试一次。
		v.resurrected = false
		runtime.SetFinalizer(v, finalize)
		return
	}
	delete(valMap, keyFor(v.cmpVal))
}

// 如果你不要求未使用的值是
// 垃圾回收的，那么实习很简单。但我们确实要求这样做；我们不想成为
// DOS向量。我们通过使用uintptr对垃圾收集器
// 隐藏指针，并在没有其他代码使用它时使用终结器消除
// 指针来实现这一点。
// 
// 最明显的实现方式是使用
// 映射-*接口{}的[interface{}]uintptr，并设置一个终结器，将
// 从映射中删除。不幸的是，这很有意思。由于指针
// 的创建违反了Go的安全规则，因此
// 可以与GC 
// 同时创建指向值的指针，从而得出可以收集该值的结论。还有其他种族
// 也打破了等式不变量，但在释放
// 后使用将导致运行时崩溃。
// 
// 若要执行此操作，终结器需要知道，自终结器设置以来，没有不安全地创建引用
// 。要做到这一点，值带有一个“复活的”哨兵，每当指针被不安全地创建时，它就会被设置为
// 的。如果终结器遇到
// 哨兵，它将清除哨兵，并通过将自身重新安装为终结器来延迟一个
// 额外GC周期的收集。此
// 确保GC可以看到不安全创建的指针，并且
// 将正确阻止收集。
// 
// 这种技术确实意味着被重用的内部值需要至少3个GC循环才能完全收集（1个清除哨兵，1个清除不安全地图，1个实际删除）。
// 
// @ianlancetaylor在
// https:
// 在
// 终结器中实现弱引用是可能的，而不需要不安全的。不幸的是，他概述的
// 方法在这里不起作用，原因有二。首先，没有办法用弱指针构造强指针；我们的映射存储
// 弱指针，但我们必须将强指针返回给调用方。
// 其次，更根本的是，我们不仅必须返回指向调用方的强
// 指针，而且必须返回指向调用方的相同强指针。在
// 中，为了向调用者返回相同的强指针，我们必须跟踪
// 它，这正是我们不能使用强指针的地方。
// 参见https:
// 讨论，以及https:
// 的微妙之处的说明。
